package subumbo.audio.generator
{
	import __AS3__.vec.Vector;
	
	import flash.events.Event;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;
	
	import subumbo.audio.core.IAudioProcessor;
	import subumbo.audio.driver.AudioDriver;

	public class Turntable extends SoundLoader implements IAudioProcessor
	{	
		private var _target: ByteArray;
		private var _id:String;
		
		public var numEffects:int = 0;
		
		private var _deactivatedFunction:Function;
		private var _activateFucntion:Function;
		private var _addEffectFunction:Function;
		private var _removeEffectFunction:Function
		
		private var _processIndex:int;
		
		private var _activated:Boolean;
		private var _oldActivated:Boolean;
		
		private var _vL:Number = 1;
		private var _vR:Number = 1;
		private var _oldVR:Number = 1;
		private var _oldVL:Number = 1;
		private var _pan:Number = 0.5;
		//private var _position:int = 0;
		
		private var _position: Number = 0;
		private var _rate: Number = 1.0;
		private var _reverse:Boolean;
		
		private var _deactivating:Boolean;
		
		public function Turntable(id:String, url:String=null)
		{
			_id = id;
			if(url)load(url);	
		}
		
		override protected function loadComplete(event:Event):void
		{
			_target = new ByteArray();
			super.loadComplete(event);
		}
		
		public function process(buffer: Vector.<Vector.<Number>>):void
		{
				var tempLe:Vector.<Number> = new Vector.<Number>(AudioDriver.SIZE, true);
				var tempRi:Vector.<Number> = new Vector.<Number>(AudioDriver.SIZE, true);
				var le: Vector.<Number> = buffer[0];
				var ri: Vector.<Number> = buffer[1];
			if(_loaded && _activated)
			{
				if(_rate < 0)
				{
					_target.position = 0;
					
					var rate:Number = _rate * -1;
					//-- SHORTCUT
					//var data: ByteArray = event.data;
					
					var scaledBlockSize: Number = AudioDriver.SIZE * rate;
					var positionInt: int = _position;
					var alpha: Number = _position - positionInt;
					alpha *= -1;
					var positionTargetNum: Number = alpha;
					var positionTargetInt: int = -1;
		
					//-- COMPUTE NUMBER OF SAMPLES NEED TO PROCESS BLOCK (+2 FOR INTERPOLATION)
					var need: int = Math.ceil( scaledBlockSize ) + 2;
					
					//-- EXTRACT SAMPLES
					trace(positionInt)
					var read: int = _sound.extract( _target, need, positionInt );
		
					var n: int = read == need ? AudioDriver.SIZE : read / rate;
		
					var l0: Number;
					var r0: Number;
					var l1: Number;
					var r1: Number;
					for( var i: int = 0 ; i < n ; ++i )
					{
						//-- AVOID READING EQUAL SAMPLES, IF RATE < 1.0
						if( int( positionTargetNum ) != positionTargetInt )
						{
							positionTargetInt = positionTargetNum;
							
							//-- SET TARGET READ POSITION
							_target.position = positionTargetInt << 3;
			
							//-- READ TWO STEREO SAMPLES FOR LINEAR INTERPOLATION
							l0 = _target.readFloat();
							r0 = _target.readFloat();
		
							l1 = _target.readFloat();
							r1 = _target.readFloat();
						}
						
						//-- WRITE INTERPOLATED AMPLITUDES INTO STREAM
						tempLe[i] = (l0 - alpha * (  l0 - l1 )) * _vL ;
						tempRi[i] = (r0 - alpha * ( r0 - r1 )) * _vR ;
						
						//-- INCREASE TARGET POSITION
						positionTargetNum += rate;
						
						//-- INCREASE FRACTION AND CLAMP BETWEEN 0 AND 1
						alpha += rate;
						while( alpha >= 1.0 ) --alpha;
					}
					
					i=0;
						var t:int = n-1;
						for( ; i < n ; ++i )
						{
							le[i] = tempLe[t-i]
							ri[i] = tempRi[t-i]
						}
					
					//-- FILL REST OF STREAM WITH ZEROs
					/* if( i < AudioDriver.SIZE )
					{
						while( i < AudioDriver.SIZE )
						{
							le[i] = 0.0 ;
							ri[i] = 0.0;
							
							++i;
						}
					} */
		
					//-- INCREASE SOUND POSITION
					if(!_reverse) 
					{
						_reverse = true;
						_position -= 2*scaledBlockSize;
					}else _position -= scaledBlockSize;
				}
				if(_rate >= 0) 
				{
					if(_reverse) {_reverse = false;}
					_target.position = 0;
					
					//-- SHORTCUT
					//var data: ByteArray = event.data;
					
					var scaledBlockSize: Number = AudioDriver.SIZE * _rate;
					var positionInt: int = _position;
					var alpha: Number = _position - positionInt;
		
					var positionTargetNum: Number = alpha;
					var positionTargetInt: int = -1;
		
					//-- COMPUTE NUMBER OF SAMPLES NEED TO PROCESS BLOCK (+2 FOR INTERPOLATION)
					var need: int = Math.ceil( scaledBlockSize ) + 2;
					
					//-- EXTRACT SAMPLES
					var read: int = _sound.extract( _target, need, positionInt );
		
					var n: int = read == need ? AudioDriver.SIZE : read / _rate;
		
					var l0: Number;
					var r0: Number;
					var l1: Number;
					var r1: Number;
					for( var i: int = 0 ; i < n ; ++i )
					{
						//-- AVOID READING EQUAL SAMPLES, IF RATE < 1.0
						if( int( positionTargetNum ) != positionTargetInt )
						{
							positionTargetInt = positionTargetNum;
							
							//-- SET TARGET READ POSITION
							_target.position = positionTargetInt << 3;
			
							//-- READ TWO STEREO SAMPLES FOR LINEAR INTERPOLATION
							l0 = _target.readFloat();
							r0 = _target.readFloat();
		
							l1 = _target.readFloat();
							r1 = _target.readFloat();
						}
						
						//-- WRITE INTERPOLATED AMPLITUDES INTO STREAM
						le[i] = (l0 + alpha * ( l1 - l0 )) * _vL ;
						ri[i] = (r0 + alpha * ( r1 - r0 )) * _vR ;
						
						//-- INCREASE TARGET POSITION
						positionTargetNum += _rate;
						
						//-- INCREASE FRACTION AND CLAMP BETWEEN 0 AND 1
						alpha += _rate;
						while( alpha >= 1.0 ) --alpha;
					}
					
					//-- FILL REST OF STREAM WITH ZEROs
					if( i < AudioDriver.SIZE )
					{
						while( i < AudioDriver.SIZE )
						{
							le[i] = 0.0 ;
							ri[i] = 0.0;
							
							++i;
						}
					}
		
					//-- INCREASE SOUND POSITION
					_position += scaledBlockSize;
				}
				
				if(_deactivating)
				{
						i = AudioDriver.SIZE;
						while( --i > -1 )
						{
							le[i] = 0.0 ;
							ri[i] = 0.0;
						}
					
				}
			}else
			{
				i = AudioDriver.SIZE;
						while( --i > -1 )
						{
							le[i] = 0.0 ;
							ri[i] = 0.0;
						}
			}
		}
		
		public function deactivate():void
		{
			_activated = false;	
			setTimeout(reactivated,140);
		}
		
		private function reactivated():void
		{
			_deactivatedFunction.call(null,this);
		}
		
		
		public function pitch(rate:Number):void
		{
			_rate = rate;
		}
		
		public function activate():void
		{
			_activateFucntion.call(null,this);
			_activated = true;
		}
		
		public function setActivated(boo:Boolean):void
		{
			_activated = boo;
		}
		
		
		public function addEffect(effect:IAudioProcessor):IAudioProcessor
		{
			_addEffectFunction.call(null, effect, _id);
			return effect;
		}
		
		public function removeEffect(obj:Object):void
		{
			_removeEffectFunction.call(null, this);
		}
		
		public function flush():void
		{
			_target = null;
			_id = null;
			_deactivatedFunction = null;
			_activateFucntion = null;
			_removeEffectFunction = null;
			_deactivatedFunction = null;
		}
		
		public function set processIndex(val:int):void
		{
			_processIndex = val;
		}
		
		public function get processIndex():int
		{
			return _processIndex;
		}
		
		private function updateVolume():void
		{
			_vL = _oldVL * 1-_pan;
			_vR = _oldVL * _pan;
		}
		
		public function set addEffectFunction(val:Function):void { _addEffectFunction = val; }
		public function set removeEffectFunction(val:Function):void { _removeEffectFunction = val; }
		public function set activateFunction(val:Function):void { _activateFucntion = val; }
		public function set deactivatedFunction(val:Function):void { _deactivatedFunction = val; }
		
		public function set volume(val:Number):void { _oldVL = _oldVR = val; updateVolume(); }
		public function set pan(val:Number):void { _pan = val; updateVolume(); }
		public function set position(val:Number):void { _position = val; }
		public function set id(val:String):void {_id = val; }
		public function get id():String { return _id; }
	}
}